%{
  /* Aqui, pode-se inserir qualquer codigo C necessario ah compilacao
   * final do parser. Sera copiado tal como esta no inicio do y.tab.c
   * gerado por Yacc.
   */
  #include <stdio.h>
  #include <stdlib.h>
  #include <string.h>
  #include "node.h"
  #include "lista.h"
  #include "symbol_table.h"
	
  #include "attributes.h"

int lineno;

void print_tree(Node* node);
void insere_tabela(Node* nodo, Node* tipo);
char* novo_tmp();
char* novo_rot();
void formata (char* retorno, int i, char* reg);
attr_exprbool* calcula_booleano(Node* nodo, char* t, char* fim);

symbol_t symbol_table;
int desloc = 0;

	
%}

%union {
  char* cadeia;
  struct _node * no;
	
}


%token<cadeia> IDF
%token<cadeia> INT
%token<cadeia> DOUBLE
%token<cadeia> FLOAT
%token<cadeia> CHAR
%token QUOTE
%token DQUOTE
%token LE
%token GE
%token EQ
%token NE
%token AND
%token OR
%token NOT
%token IF
%token THEN
%token ELSE
%token WHILE
%token<cadeia> INT_LIT
%token<cadeia> F_LIT
%token END
%token<cadeia> TRUE
%token<cadeia> FALSE

%type<no> code 
%type<no> acoes
%type<no> comando 
%type<no> enunciado
%type<no> expr
%type<no> listaexpr
%type<no> lvalue
%type<no> chamaproc
%type<no> fiminstcontrole
%type<no> expbool

%type<no> declaracoes
%type<no> declaracao
%type<no> listadeclaracao

%type<no> tipo
%type<no> tipounico
%type<no> tipolista
%type<no> listadupla

%left '+' '-'
%left '*' '/'
%left OR
%left AND
%left NOT

%start code

 /* A completar com seus tokens - compilar com 'yacc -d' */

%%
code: declaracoes acoes { Node** children;
						  struct node_tac* atributo = NULL;
						  struct node_tac* code = ((struct node_tac *) $2->attribute);
	
						  cat_tac(&atributo,&code);
						
	print_table(symbol_table);					  
	print_tac(stderr,atributo);
	
                          pack_nodes(&children, 0, $1); 
                          pack_nodes(&children, 1, $2); 
                          syntax_tree = create_node(0, program_node, "program", atributo, 2, children);
						  $$ = syntax_tree; }
    | acoes { $$ = $1; syntax_tree = $$;  }
    ;

declaracoes: declaracao ';' { $$ = $1; }
           | declaracoes declaracao ';' { Node** children; 
                                          pack_nodes(&children, 0, $1); 
                                          pack_nodes(&children, 1, $2); 
                                          $$ = create_node(0, decl_list_node, "decl_list", NULL, 2, children);
                                        }
           ;

declaracao: listadeclaracao ':' tipo { Node** children;
									   Node* variavel = $1;
									   Node* tipo = $3;
	
									   insere_tabela(variavel,tipo);
	
									   pack_nodes(&children, 0, $1);
									   pack_nodes(&children, 1, $3);
									   $$ = create_node(0, decl_node, "decl", NULL, 2, children); 
									   }
							;
																			
listadeclaracao: IDF     { $$ = create_leaf(0, idf_node, $1, NULL);}
               | IDF ',' listadeclaracao { Node** children;
                                            pack_nodes(&children, 0, create_leaf(0, idf_node, $1, NULL));
                                            pack_nodes(&children, 1, $3);
											$$ = create_node(0, idf_list_node, "idf_list", NULL, 2, children); } 
               ;

tipo: tipounico { $$ = $1; }
    | tipolista { $$ = $1; }
    ;

tipounico: INT    { attr_tipo* atributo = malloc(sizeof(attr_tipo));
					atributo->tipo = INT_TYPE;
					atributo->size = INT_SIZE;
					$$ = create_leaf(0, canonicaltype_node, "int", atributo);}
		 | DOUBLE { attr_tipo* atributo = malloc(sizeof(attr_tipo));
					atributo->tipo = DOUBLE_TYPE;
					atributo->size = DOUBLE_SIZE;
					$$ = create_leaf(0, canonicaltype_node, "double", atributo);}
		 | FLOAT  { attr_tipo* atributo = malloc(sizeof(attr_tipo));
					atributo->tipo = FLOAT_TYPE;
					atributo->size = FLOAT_SIZE;
					$$ = create_leaf(0, canonicaltype_node, "float", atributo);}
		 | CHAR   { attr_tipo* atributo = malloc(sizeof(attr_tipo));
					atributo->tipo = CHAR_TYPE;
					atributo->size = CHAR_SIZE;
					$$ = create_leaf(0, canonicaltype_node, "char", atributo);} 
         ;

tipolista: INT '[' listadupla ']' { Node** children;
									attr_tipocomposto* atributo = malloc(sizeof(attr_tipocomposto));
									attr_listadupla* at = ((attr_listadupla *) $3->attribute);
	
									atributo->tipo = INT_TYPE;
									atributo->tamanho = at->n_elementos * INT_SIZE;
									atributo->w = INT_SIZE;
									atributo->lista = at;	
	
									pack_nodes(&children, 0, create_leaf(0, type_node, "int", NULL));
									pack_nodes(&children, 1, $3);
	
									$$ = create_node(0, arraytype_node, "int lista", atributo, 2, children);
									}
		| DOUBLE '[' listadupla ']' { Node** children;
									  attr_tipocomposto* atributo = malloc(sizeof(attr_tipocomposto));
									  attr_listadupla* at = ((attr_listadupla *) $3->attribute);
			
									  atributo->tipo = DOUBLE_TYPE;
									  atributo->tamanho = at->n_elementos * DOUBLE_SIZE;
									  atributo->w = DOUBLE_SIZE;
									  atributo->lista = at;			
			
									  pack_nodes(&children, 0, create_leaf(0, type_node, "double", NULL));
									  pack_nodes(&children, 1, $3);
			
									  $$ = create_node(0, arraytype_node, "double lista", atributo, 2, children);
									  }
		| FLOAT '[' listadupla ']' { Node** children;			
									 attr_tipocomposto* atributo = malloc(sizeof(attr_tipocomposto));
									 attr_listadupla* at = ((attr_listadupla *) $3->attribute);
			
									 atributo->tipo = FLOAT_TYPE;
									 atributo->tamanho = at->n_elementos * FLOAT_SIZE;
								     atributo->w = FLOAT_SIZE;
									 atributo->lista = at;
			
									 pack_nodes(&children, 0, create_leaf(0, type_node, "float", NULL));
									 pack_nodes(&children, 1, $3);
			
									  $$ = create_node(0, arraytype_node, "float lista", atributo, 2, children);
									}
		| CHAR '[' listadupla ']' { Node** children;			
									attr_tipocomposto* atributo = malloc(sizeof(attr_tipocomposto));
									attr_listadupla* at = ((attr_listadupla *) $3->attribute);
			
									atributo->tipo = CHAR_TYPE;
									atributo->tamanho = at->n_elementos * CHAR_SIZE;
									atributo->w = CHAR_SIZE;
									atributo->lista = at;
			
									pack_nodes(&children, 0, create_leaf(0, type_node, "char", NULL));
									pack_nodes(&children, 1, $3);
			
									$$ = create_node(0, arraytype_node, "char lista", atributo, 2, children);
									}
         ;

listadupla: INT_LIT ':' INT_LIT { Node** children;
								  attr_listadupla* atributo = malloc(sizeof(attr_listadupla));
	
								  atributo->inicio = malloc(sizeof(int));
								  atributo->inicio[0] = atoi($1);
								  atributo->n_dimensoes = 1;
								  atributo->tamanho = malloc(sizeof(int));
								  atributo->tamanho[0] = atoi($3) - atributo->inicio[0] +1;
								  atributo->n_elementos = atributo->tamanho[0];
	 
								  pack_nodes(&children, 0, create_leaf(0, int_node, $1, NULL));
								  pack_nodes(&children, 1, create_leaf(0, int_node, $3, NULL));
	
								  $$ = create_node(0, listadupla_node, "lista dupla simples", atributo, 2, children);
								  }
		| INT_LIT ':' INT_LIT ',' listadupla { int i;
											   Node** children;
											   attr_listadupla* atributo = malloc(sizeof(attr_listadupla));
											   attr_listadupla* at = ((attr_listadupla *) $5->attribute);
			
											   atributo->n_dimensoes = at->n_dimensoes + 1;
											   atributo->inicio = malloc(sizeof(int) * atributo->n_dimensoes);
											   atributo->inicio[0] = atoi($1);
											   for (i=0; i < (atributo->n_dimensoes - 1); i++)
												   atributo->inicio[i+1] = at->inicio[i];
											   atributo->tamanho = malloc(sizeof(int) * atributo->n_dimensoes);
											   atributo->tamanho[0] = atoi($3) - atributo->inicio[0] + 1;
											   for (i=0; i < (atributo->n_dimensoes - 1); i++)
												   atributo->tamanho[i+1] = at->tamanho[i];
											   atributo->n_elementos = atributo->tamanho[0] * at->n_dimensoes;
			
											   pack_nodes(&children, 0, create_leaf(0, int_node, $1, NULL));
											   pack_nodes(&children, 1, create_leaf(0, int_node, $3, NULL));
											   pack_nodes(&children, 2, $5);
												   
											   $$ = create_node(0, listadupla_node, "lista dupla composta", atributo, 3, children);
											   }
          ;

acoes: comando ';'  { $$ = $1; }
	 | comando ';' acoes { Node** children;
						   struct node_tac* atributo = NULL;
						   struct node_tac* code1 = ((struct node_tac *) $1->attribute);
						   struct node_tac* code2 = ((struct node_tac *) $3->attribute);
		
						   cat_tac(&atributo,&code1);
						   cat_tac(&atributo,&code2);
		
						   pack_nodes(&children, 0, $1);
						   pack_nodes(&children, 1, $3);
		 
						   $$ = create_node(0, acoes_node, "acoes", atributo, 2, children);
						   }
    ;

comando: lvalue '=' expr { Node** children;
						   struct node_tac* atributo = NULL;
						   attr_expr* var = ((attr_expr *) $1->attribute);
						   attr_expr* val = ((attr_expr *) $3->attribute);

						   cat_tac(&atributo,&(var->code));
						   cat_tac(&atributo,&(val->code));

						   if (var->tipo != val->tipo)
							   printf("Erro de tipo - %d / %d",var->tipo,val->tipo);

						   append_inst_tac(&atributo,create_inst_tac(TACTYPE_ATTRIB,var->local,val->local,"",""));
							
						   pack_nodes(&children, 0, $1);
						   pack_nodes(&children, 1, $3);
							   
						   $$ = create_node(0, comando_node, "atribuicao", atributo, 2, children);
						}
       | enunciado { $$ = $1;}
       ;

lvalue: IDF { attr_expr* atributo = malloc(sizeof(attr_expr));
			  entry_t* ref;
	
			  if ((ref = lookup(symbol_table,$1)) == NULL)
				  printf("variavel n declarada");				  
			  
			  atributo->local = malloc(sizeof(char)*8);
			  formata(atributo->local,ref->desloc,"(SP)");
			  atributo->code = NULL;
			  atributo->tipo = ref->type;
			  $$ = create_leaf(0, idf_node, $1, atributo);} 
	| IDF '[' listaexpr ']' { Node** children;
							  attr_expr* atributo = malloc(sizeof(attr_expr));
							  attr_listaexpr* at = ((attr_listaexpr *) $3->attribute);
							  entry_t* ref;
							  int i;
							  char* res;
							  char* n;
							  attr_array* array_info;
		
							  if((ref = lookup(symbol_table,$1)) == NULL) {
								 printf("simbolo nao encontrado na tabela de simbolos");
								 return -1;
							  }
		
							  array_info = ((attr_array *) ref->extra);
	
							  if(array_info == NULL) {
								printf("simbolo nao e um array");
								return -1;
							  }
		
							  if (at->tamanho > array_info->n_dimensoes) {
								  printf("array indexado em mais posicoes que possui");
								  return -1;
							  }
		
							  atributo->code = NULL;
							  res = novo_tmp(INT_TYPE,"(Rx)");
		
							  append_inst_tac(&(atributo->code),create_inst_tac(TACTYPE_ATTRIB,res,at->expr[0]->local,"",""));
		
							  n = malloc(sizeof(char) * 12);
		
							  for (i = 0; i < at->tamanho; i++){
								  sprintf(n, "%d", array_info->tamanho[i]);
								  append_inst_tac(&(atributo->code),create_inst_tac(TACTYPE_ATTRIB,res,res,"*",n));
								  append_inst_tac(&(atributo->code),create_inst_tac(TACTYPE_ATTRIB,res,res,"+",at->expr[i]->local));
							  }
		
							  sprintf(n, "%d", array_info->w);
							  append_inst_tac(&(atributo->code),create_inst_tac(TACTYPE_ATTRIB,res,res,"*",n));
		
							  if (array_info->c < 0) {
								 sprintf(n, "%d", (0 - array_info->c));
								 append_inst_tac(&(atributo->code),create_inst_tac(TACTYPE_ATTRIB,res,res,"-",n));
							  } else {
								  sprintf(n, "%d", array_info->c);
								  append_inst_tac(&(atributo->code),create_inst_tac(TACTYPE_ATTRIB,res,res,"+",n));
							  }
		
							  atributo->local = malloc(sizeof(char) * 17);
							  strcpy(atributo->local, res);
							  strcat(atributo->local, "(000(SP))");
							  atributo->tipo = ref->type;
		
							  pack_nodes(&children, 0, create_leaf(0, idf_node, $1, NULL));
							  pack_nodes(&children, 1, $3);
							  $$ = create_node(0, lvalue_node, "lvalue", atributo, 2, children);
							  }
      ;

listaexpr: expr { Node** children;
				  attr_listaexpr* atributo = malloc(sizeof(attr_listaexpr));
	
				  atributo->tamanho = 1;
				  atributo->expr = malloc(sizeof(attr_expr));
	
				  atributo->expr[0] = ((attr_expr *) $1->attribute);
	
				  pack_nodes(&children, 0, $1);
				  $$ = create_node(0, listaexpr_node, "listaexpr", atributo, 1, children);
				}
	| expr ',' listaexpr { Node** children;
						   attr_listaexpr* atributo = malloc(sizeof(attr_listaexpr));
						   attr_listaexpr* at = ((attr_listaexpr *) $3->attribute);
						   int i;
		
						   atributo->tamanho = at->tamanho + 1;
						   atributo->expr = malloc(sizeof(attr_expr *) * at->tamanho);
		
						   atributo->expr[0] = ((attr_expr *) $1->attribute);
						   for (i = 0; i < at->tamanho; i++)
							   atributo->expr[i+1] = at->expr[i];
		
						   pack_nodes(&children, 0, $1);
						   pack_nodes(&children, 1, $3);
						   $$ = create_node(0, listaexpr_node, "listaexpr", atributo, 2, children);
						 }
	   ;

expr: expr '+' expr { Node** children;
					  attr_expr* atributo = malloc(sizeof(attr_expr));
					  attr_expr* expr1 = ((attr_expr *) $1->attribute);
					  attr_expr* expr2 = ((attr_expr *) $3->attribute);
	
					  cat_tac(&(atributo->code),&(expr1->code));
					  cat_tac(&(atributo->code),&(expr2->code));

					  if (expr1->tipo != expr2->tipo) {
						  printf("Erro de tipo - %d / %d",expr1->tipo,expr2->tipo);	
						  return -1;
					  }
	
					  atributo->local = novo_tmp(expr1->tipo,"(Rx)");
					  append_inst_tac(&(atributo->code),create_inst_tac(TACTYPE_ATTRIB,atributo->local,expr1->local,"+",expr2->local));
					  atributo->tipo = expr1->tipo;

					  pack_nodes(&children, 0, $1);
					  pack_nodes(&children, 1, $3);
					  $$ = create_node(0, plus_node, "plus", atributo, 2, children);
					}
	| expr '-' expr { Node** children;
					  attr_expr* atributo = malloc(sizeof(attr_expr));
					  attr_expr* expr1 = ((attr_expr *) $1->attribute);
					  attr_expr* expr2 = ((attr_expr *) $3->attribute);
		
					  if (expr1->tipo != expr2->tipo) {
						  printf("Erro de tipo - %d / %d",expr1->tipo,expr2->tipo);	
						  return -1;
					  }
		
					  cat_tac(&(atributo->code),&(expr1->code));
					  cat_tac(&(atributo->code),&(expr2->code));
		
					  atributo->local = novo_tmp(expr1->tipo,"(Rx)");
					  append_inst_tac(&(atributo->code),create_inst_tac(TACTYPE_ATTRIB,atributo->local,expr1->local,"-",expr2->local));
					  atributo->tipo = expr1->tipo;
		
					  pack_nodes(&children, 0, $1);
					  pack_nodes(&children, 1, $3);
					  $$ = create_node(0, minus_node, "minus", atributo, 2, children);
					}
	| expr '*' expr { Node** children;
					  attr_expr* atributo = malloc(sizeof(attr_expr));
					  attr_expr* expr1 = ((attr_expr *) $1->attribute);
					  attr_expr* expr2 = ((attr_expr *) $3->attribute);
		
					  cat_tac(&(atributo->code),&(expr1->code));
					  cat_tac(&(atributo->code),&(expr2->code));
		
					  if (expr1->tipo != expr2->tipo) {
						  printf("Erro de tipo - %d / %d",expr1->tipo,expr2->tipo);	
						  return -1;
					  }
		
					  atributo->local = novo_tmp(expr1->tipo,"(Rx)");
					  append_inst_tac(&(atributo->code),create_inst_tac(TACTYPE_ATTRIB,atributo->local,expr1->local,"*",expr2->local));
					  atributo->tipo = expr1->tipo;		
		
					  pack_nodes(&children, 0, $1);
					  pack_nodes(&children, 1, $3);
					  $$ = create_node(0, mult_node, "mult", atributo, 2, children);
					}
	| expr '/' expr { Node** children;
					  attr_expr* atributo = malloc(sizeof(attr_expr));
					  attr_expr* expr1 = ((attr_expr *) $1->attribute);
					  attr_expr* expr2 = ((attr_expr *) $3->attribute);
		
					  cat_tac(&(atributo->code),&(expr1->code));
					  cat_tac(&(atributo->code),&(expr2->code));
		
					  if (expr1->tipo != expr2->tipo) {
						  printf("Erro de tipo - %d / %d",expr1->tipo,expr2->tipo);	
						  return -1;
					  }
		
					  atributo->local = novo_tmp(expr1->tipo,"(Rx)");
					  append_inst_tac(&(atributo->code),create_inst_tac(TACTYPE_ATTRIB,atributo->local,expr1->local,"/",expr2->local));
					  atributo->tipo = expr1->tipo;
		
					  pack_nodes(&children, 0, $1);
					  pack_nodes(&children, 1, $3);
					  $$ = create_node(0, div_node, "div", NULL, 2, children);
					}
	| '(' expr ')'  { $$ = $2; }

	| INT_LIT  { attr_expr* atributo = malloc(sizeof(attr_expr));
				 atributo->local = novo_tmp(INT_TYPE,"(Rx)");
				 append_inst_tac(&(atributo->code),create_inst_tac(TACTYPE_ATTRIB,atributo->local,$1,"",""));
				 atributo->tipo = INT_TYPE;
				 $$ = create_leaf(0, int_node, $1, atributo);
				}
	| F_LIT    { attr_expr* atributo = malloc(sizeof(attr_expr));
				 atributo->local = novo_tmp(FLOAT_TYPE,"(Rx)");
				 append_inst_tac(&(atributo->code),create_inst_tac(TACTYPE_ATTRIB,atributo->local,$1,"",""));
				 atributo->tipo = FLOAT_TYPE;
				 $$ = create_leaf(0, float_node, $1, atributo);
			   } 
	| lvalue	{ $$ = $1; }
	| chamaproc { $$ = $1; }
    ;

chamaproc: IDF '(' listaexpr ')' { Node** children;
									pack_nodes(&children, 0, create_leaf(0, idf_node, $1, NULL));
									pack_nodes(&children, 1, $3);
									$$ = create_node(0, chamaproc_node, "chamada proc", NULL, 2, children);
								}
         ;

enunciado: expr { $$ = $1; }
		//| expbool { $$ = $1; }
		| IF '(' expbool ')' THEN acoes fiminstcontrole { Node** children;
														  struct node_tac* atributo = malloc(sizeof(struct node_tac));
														  attr_exprbool* at = malloc(sizeof(attr_exprbool));
														  struct node_tac* then_actions = ((struct node_tac *) $6->attribute);
														  struct node_tac* else_actions = ((struct node_tac *) $7->attribute);
			
														  char* then_label = novo_rot();
														  char* else_label = novo_rot();
														  char* next = novo_rot();
			
														  atributo = NULL;
			
														  at = calcula_booleano((Node *) $3, then_label, else_label);
			
														  cat_tac(&atributo,&(at->code));
														  append_inst_tac(&atributo,create_inst_tac(TACTYPE_LABEL,at->t,"","",""));
														  cat_tac(&atributo,&then_actions);
														  append_inst_tac(&atributo,create_inst_tac(TACTYPE_GOTO,next,"","",""));
														  append_inst_tac(&atributo,create_inst_tac(TACTYPE_LABEL,at->f,"","",""));
														  cat_tac(&atributo,&else_actions);
														  append_inst_tac(&atributo,create_inst_tac(TACTYPE_LABEL,next,"","",""));
			
														  pack_nodes(&children, 0, create_leaf(0, if_node, "if", NULL));
														  pack_nodes(&children, 1, $3);				
														  pack_nodes(&children, 2, create_leaf(0, then_node, "then", NULL));
														  pack_nodes(&children, 3, $6);
														  pack_nodes(&children, 4, $7);
														  $$ = create_node(0, enunciado_node, "enunciado", atributo, 5, children);
														}
		| WHILE '(' expbool ')' '{' acoes '}' { Node** children;
												struct node_tac* atributo = malloc(sizeof(struct node_tac));
												attr_exprbool* at = malloc(sizeof(attr_exprbool));
												struct node_tac* acoes_while = ((struct node_tac *) $6->attribute);
			
												char* begin = novo_rot();
												char* true_label = novo_rot();
												char* next = novo_rot();
			
												atributo = NULL;
			
												at = calcula_booleano((Node *) $3, true_label, next);
			
												append_inst_tac(&atributo,create_inst_tac(TACTYPE_LABEL,begin,"","",""));
												cat_tac(&atributo,&(at->code));
												append_inst_tac(&atributo,create_inst_tac(TACTYPE_LABEL,at->t,"","",""));
												cat_tac(&atributo,&acoes_while);
												append_inst_tac(&atributo,create_inst_tac(TACTYPE_GOTO,begin,"","",""));
												append_inst_tac(&atributo,create_inst_tac(TACTYPE_LABEL,at->f,"","",""));
			
												pack_nodes(&children, 0, create_leaf(0, expbool_node, "while", NULL));
												pack_nodes(&children, 1, $3);
												pack_nodes(&children, 2, $6);
												$$ = create_node(0, enunciado_node, "enunciado", atributo, 3, children);
												}
         ;

fiminstcontrole: END { $$ = create_leaf(0, end_node, "end", NULL); } 
		| ELSE acoes END { Node** children;
						   struct node_tac* atributo = malloc(sizeof(struct node_tac));
						   struct node_tac* acao = ((struct node_tac *) $2->attribute);
			
						   atributo = NULL;
						   cat_tac(&atributo,&acao);
			
						   pack_nodes(&children, 0, create_leaf(0, else_node, "else", NULL));
						   pack_nodes(&children, 1, $2);				
						   pack_nodes(&children, 2, create_leaf(0, end_node, "end", NULL));
						   $$ = create_node(0, fiminstcontrole_node, "fim inst ctl", atributo, 3, children);
						}
               ;

expbool: TRUE { $$ = create_leaf(0, true_node, "true", NULL); } 
		| FALSE { $$ = create_leaf(0, false_node, "false", NULL); } 
		| '(' expbool ')' { $$ = $2; }
		| expbool AND expbool { Node** children;
								pack_nodes(&children, 0, $1);
								pack_nodes(&children, 1, $3);
								$$ = create_node(0, and_node, "and", NULL, 2, children);
							}
		| expbool OR expbool { Node** children;
								pack_nodes(&children, 0, $1);
								pack_nodes(&children, 1, $3);
								$$ = create_node(0, or_node, "or", NULL, 2, children);
							}
		| NOT expbool { Node** children;
						pack_nodes(&children, 0, $2);
						$$ = create_node(0, not_node, "not", NULL, 1, children);
					}
		| expr '>' expr { Node** children;
							pack_nodes(&children, 0, $1);
							pack_nodes(&children, 1, $3);
							$$ = create_node(0, sup_node, "maior", NULL, 2, children);
						}
		| expr '<' expr { Node** children;
							pack_nodes(&children, 0, $1);
							pack_nodes(&children, 1, $3);
							$$ = create_node(0, inf_node, "menor", NULL, 2, children);
						}
		| expr LE expr { Node** children;
							pack_nodes(&children, 0, $1);
							pack_nodes(&children, 1, $3);
							$$ = create_node(0, inf_eq_node, "menor igual", NULL, 2, children);
						}
		| expr GE expr { Node** children;
							pack_nodes(&children, 0, $1);
							pack_nodes(&children, 1, $3);
							$$ = create_node(0, sup_eq_node, "maior igual", NULL, 2, children);
						}
		| expr EQ expr { Node** children;
							pack_nodes(&children, 0, $1);
							pack_nodes(&children, 1, $3);
							$$ = create_node(0, eq_node, "igualdade", NULL, 2, children);
						}
		| expr NE expr { Node** children;
							pack_nodes(&children, 0, $1);
							pack_nodes(&children, 1, $3);
							$$ = create_node(0, neq_node, "diferente", NULL, 2, children);
						}
       ;
%%
 /* A partir daqui, insere-se qlqer codigo C necessario.
  */

char* progname;
extern FILE* yyin;

int int_tmp = 0;
int int_rot = 1;

int main(int argc, char* argv[]) 
{
     syntax_tree = malloc(sizeof(Node));
	 init_table(&symbol_table);
	
	 if (argc != 2) {
     
	printf("uso: %s <input_file>. Try again!\n", argv[0]);
     exit(-1);
   }
   yyin = fopen(argv[1], "r");
   if (!yyin) {
     printf("Uso: %s <input_file>. Could not find %s. Try again!\n", 
         argv[0], argv[1]);
     exit(-1);
   }
	
   progname = argv[0];
	
	
   if (!yyparse()) {
      printf("OKAY.\n");
	  //print_tree(syntax_tree);   
   }else {
      printf("ERROR.\n");
   }
   
   fclose(yyin);
   return(0);
}

void formata (char* retorno, int i, char* reg) {
	sprintf(retorno,"%03d%s",i,reg);
}

char* novo_tmp(int tipo, char* reg){
	
	int size;
	char* tmp = malloc(sizeof(char) * 10);
	char* retorno;
	
	if (tipo == INT_TYPE)
		size = INT_SIZE;
	else if (tipo == DOUBLE_TYPE)
			 size = DOUBLE_SIZE;
	else if (tipo == FLOAT_TYPE)
		     size = FLOAT_SIZE;
	else size = CHAR_SIZE;

	formata(tmp,int_tmp,reg);
	
	int_tmp += size;
	
	retorno = malloc(strlen(tmp));
	strcpy(retorno,tmp);
	return retorno;	
}

char* novo_rot(){
	
	char* tmp;
	char* retorno;
	tmp = malloc(10 * sizeof(char));
	sprintf(tmp,"LABEL%d",int_rot++);
	retorno = malloc(strlen(tmp));
	strcpy(retorno,tmp);
	return retorno;
	
}

void print_tree(Node* node){
  fprintf(stderr, "%d -> %s\n", node->type, node->lexeme);
  int i = 0;
  for (i = 0; i < node->nb_children; i++){
    print_tree(child(node, i));
  }
}

yyerror(char* s) {
  fprintf(stderr, "%s: %s", progname, s);
  fprintf(stderr, "line %d\n", lineno);
}

void insere_tabela(Node* nodo, Node* tipo) {
	if (nodo->type == idf_node) {
		
		entry_t* entrada = malloc(sizeof(entry_t));
		entrada->name = malloc(sizeof(char) * (strlen(nodo->lexeme) + 1));
		
		strcpy(entrada->name,nodo->lexeme);
		
		if (tipo->type == canonicaltype_node) {
			attr_tipo* aux = (attr_tipo *) tipo->attribute;
			
			entrada->type = aux->tipo;
			entrada->size = aux->size;
			entrada->extra = NULL;
		} else if (tipo->type == arraytype_node) {
			int c;
			int i;
			attr_tipocomposto* aux = (attr_tipocomposto *) tipo->attribute;
			attr_listadupla* aux_tipo = aux->lista;
			attr_array* info_array = malloc(sizeof(attr_array));
			
			c = aux_tipo->inicio[0];
			for (i = 1; i < aux_tipo->n_dimensoes; i++)
				c = aux_tipo->tamanho[i] + aux_tipo->inicio[i];
			c = c * aux->w;
			c = desloc - c;
			
			info_array->c = c;
			info_array->n_dimensoes = aux_tipo->n_dimensoes;
			info_array->tamanho = malloc(sizeof(int) * info_array->n_dimensoes);
			for (i = 1; i < info_array->n_dimensoes; i++)
				info_array->tamanho[i] = aux_tipo->tamanho[i];
			info_array->w = aux->w;
			
			entrada->type = aux->tipo;
			entrada->size = aux->tamanho;
			entrada->extra = info_array;
		}
		entrada->desloc = desloc;
		desloc += entrada->size;
		insert(&symbol_table,entrada);
	} else {
			int i;
			for (i = 0; i < nb_of_children(nodo); i++)
				insere_tabela(child(nodo,i),tipo);
		
	}
}

attr_exprbool* calcula_booleano(Node* nodo, char* t, char* fim) {
	
	attr_exprbool* temp;
	
	if(nodo->type == true_node) {
		temp = malloc(sizeof(attr_exprbool));
		struct node_tac* code = malloc(sizeof(struct node_tac));
		temp->t = t;
		temp->f = fim;
		code = NULL;
		append_inst_tac(&code,create_inst_tac(TACTYPE_GOTO,temp->t,"","",""));
		temp->code = code;
		return temp;
	}
	if(nodo->type == false_node) {
		temp = malloc(sizeof(attr_exprbool));
		struct node_tac* code = malloc(sizeof(struct node_tac));
		temp->t = t;
		temp->f = fim;
		code = NULL;
		append_inst_tac(&code,create_inst_tac(TACTYPE_GOTO,temp->f,"","",""));
		temp->code = code;
		return temp;
	}
	if(nodo->type == and_node) {
		temp = malloc(sizeof(attr_exprbool));
		struct node_tac* code = malloc(sizeof(struct node_tac));
		struct node_tac* code_left = malloc(sizeof(struct node_tac));
		struct node_tac* code_right = malloc(sizeof(struct node_tac));
		attr_exprbool* left = malloc(sizeof(attr_exprbool));
		attr_exprbool* right = malloc(sizeof(attr_exprbool));
		char* novo = novo_rot();
		
		left = calcula_booleano((Node *)nodo->children[0],novo,fim);
		right = calcula_booleano((Node *)nodo->children[1],t,fim);
		
		temp->t = t;
		temp->f = fim;
		
		code = NULL;
		
		cat_tac(&code,&(left->code));
		append_inst_tac(&code,create_inst_tac(TACTYPE_LABEL,left->t,"","",""));
		cat_tac(&code,&(right->code));
		temp->code = code;
		return temp;
	}
	if(nodo->type == or_node) {
		temp = malloc(sizeof(attr_exprbool));
		struct node_tac* code = malloc(sizeof(struct node_tac));
		struct node_tac* code_left = malloc(sizeof(struct node_tac));
		struct node_tac* code_right = malloc(sizeof(struct node_tac));
		attr_exprbool* left = malloc(sizeof(attr_exprbool));
		attr_exprbool* right = malloc(sizeof(attr_exprbool));
		char* novo = novo_rot();
		
		left = calcula_booleano((Node *)nodo->children[0],t,novo);
		
		right = calcula_booleano((Node *)nodo->children[1],t,fim);
		
		temp->t = t;
		temp->f = fim;
		
		code = NULL;
		
		cat_tac(&code,&(left->code));
		append_inst_tac(&code,create_inst_tac(TACTYPE_LABEL,left->f,"","",""));
		cat_tac(&code,&(right->code));
		temp->code = code;
		return temp;
	}
	if(nodo->type == not_node) {
		struct node_tac* code = malloc(sizeof(struct node_tac));
		attr_exprbool* atributo = malloc(sizeof(attr_exprbool));
		attr_exprbool* not_atributo = malloc(sizeof(attr_exprbool));
		
		atributo = calcula_booleano((Node *)nodo->children[0],t,fim);
		not_atributo->t = atributo->f;
		not_atributo->f = atributo->t;
		
		code = NULL;
		
		cat_tac(&code,&(atributo->code));
		
		not_atributo->code = code;
		return not_atributo;
	}
	if(nodo->type == sup_node) {
		struct node_tac* code = malloc(sizeof(struct node_tac));
		attr_exprbool* atributo = malloc(sizeof(attr_exprbool));
		attr_expr* expr1 = (attr_expr *) nodo->children[0]->attribute;
		attr_expr* expr2 = (attr_expr *) nodo->children[1]->attribute;
		
		atributo->t = t;
		atributo->f = fim;
		
		code = NULL;
		
		cat_tac(&code,&(expr1->code));
		cat_tac(&code,&(expr2->code));
		append_inst_tac(&code,create_inst_tac(TACTYPE_IF,atributo->t,expr1->local,">",expr2->local));
		append_inst_tac(&code,create_inst_tac(TACTYPE_GOTO,atributo->f,"","",""));
		
		atributo->code = code;
		
		return atributo;
		
		
	}
	if(nodo->type == inf_node) {
		struct node_tac* code = malloc(sizeof(struct node_tac));
		attr_exprbool* atributo = malloc(sizeof(attr_exprbool));
		attr_expr* expr1 = (attr_expr *) nodo->children[0]->attribute;
		attr_expr* expr2 = (attr_expr *) nodo->children[1]->attribute;
		
		atributo->t = t;
		atributo->f = fim;
		
		code = NULL;
		
		cat_tac(&code,&(expr1->code));
		cat_tac(&code,&(expr2->code));
		append_inst_tac(&code,create_inst_tac(TACTYPE_IF,atributo->t,expr1->local,"<",expr2->local));
		append_inst_tac(&code,create_inst_tac(TACTYPE_GOTO,atributo->f,"","",""));
		
		atributo->code = code;
		
		return atributo;
		
		
	}
	if(nodo->type == sup_eq_node) {
		struct node_tac* code = malloc(sizeof(struct node_tac));
		attr_exprbool* atributo = malloc(sizeof(attr_exprbool));
		attr_expr* expr1 = (attr_expr *) nodo->children[0]->attribute;
		attr_expr* expr2 = (attr_expr *) nodo->children[1]->attribute;
		
		atributo->t = t;
		atributo->f = fim;
		
		code = NULL;
		
		cat_tac(&code,&(expr1->code));
		cat_tac(&code,&(expr2->code));
		append_inst_tac(&code,create_inst_tac(TACTYPE_IF,atributo->t,expr1->local,">=",expr2->local));
		append_inst_tac(&code,create_inst_tac(TACTYPE_GOTO,atributo->f,"","",""));
		
		atributo->code = code;
		
		return atributo;
		
		
	}
	if(nodo->type == inf_eq_node) {
		struct node_tac* code = malloc(sizeof(struct node_tac));
		attr_exprbool* atributo = malloc(sizeof(attr_exprbool));
		attr_expr* expr1 = (attr_expr *) nodo->children[0]->attribute;
		attr_expr* expr2 = (attr_expr *) nodo->children[1]->attribute;
		
		atributo->t = t;
		atributo->f = fim;
		
		code = NULL;
		
		cat_tac(&code,&(expr1->code));
		print_tac(stderr,code);
		cat_tac(&code,&(expr2->code));
		print_tac(stderr,code);
		append_inst_tac(&code,create_inst_tac(TACTYPE_IF,atributo->t,expr1->local,"<=",expr2->local));
		append_inst_tac(&code,create_inst_tac(TACTYPE_GOTO,atributo->f,"","",""));
		
		atributo->code = code;
		
		return atributo;
		
		
	}
	if(nodo->type == eq_node) {
		struct node_tac* code = malloc(sizeof(struct node_tac));
		attr_exprbool* atributo = malloc(sizeof(attr_exprbool));
		attr_expr* expr1 = (attr_expr *) nodo->children[0]->attribute;
		attr_expr* expr2 = (attr_expr *) nodo->children[1]->attribute;
		
		atributo->t = t;
		atributo->f = fim;
		
		code = NULL;
		
		cat_tac(&code,&(expr1->code));
		cat_tac(&code,&(expr2->code));
		append_inst_tac(&code,create_inst_tac(TACTYPE_IF,atributo->t,expr1->local,"==",expr2->local));
		append_inst_tac(&code,create_inst_tac(TACTYPE_GOTO,atributo->f,"","",""));
		
		atributo->code = code;
		
		return atributo;
		
		
	}
	if(nodo->type == neq_node) {
		struct node_tac* code = malloc(sizeof(struct node_tac));
		attr_exprbool* atributo = malloc(sizeof(attr_exprbool));
		attr_expr* expr1 = (attr_expr *) nodo->children[0]->attribute;
		attr_expr* expr2 = (attr_expr *) nodo->children[1]->attribute;
		
		atributo->t = t;
		atributo->f = fim;
		
		code = NULL;
		
		cat_tac(&code,&(expr1->code));
		cat_tac(&code,&(expr2->code));
		append_inst_tac(&code,create_inst_tac(TACTYPE_IF,atributo->t,expr1->local,"!=",expr2->local));
		append_inst_tac(&code,create_inst_tac(TACTYPE_GOTO,atributo->f,"","",""));
		
		atributo->code = code;
		
		return atributo;
		
		
	}
}